#include "treeNode.h"

tree_t mTreeRoot;

void localdebug(char *str){
    #ifdef LOCAL
    fprintf(stderr, "%s", str);
    #endif
}

tree_node_t *create_and_init_node(char *name, node_data_t data, node_type_t node_type, int line_index)
{
    tree_node_t * node = (tree_node_t *)malloc(sizeof(tree_node_t));
    node->childrens = NULL;
    node->line_index = line_index;
    node->node_type = node_type;
    node->data.data_type = data.data_type;
    switch (data.data_type)
    {
    case INT_DATA:
        node->data.data = (int *)malloc(sizeof(int));
        *(int *)node->data.data = *(int *)data.data;
        break;
    case FLOAT_DATA:
        node->data.data = (float *)malloc(sizeof(float));
        *(float *)node->data.data = *(float *)data.data;
        break;
    case STRING_DATA:
        node->data.data = (char *)malloc(strlen((char *)data.data) + 1);
        strcpy((char *)node->data.data, (char *)data.data);
        break;
    default:
        node->data.data = NULL;
        fprintf(stderr, "Error: data_type is not defined.\n");
        break;
    }
    strcpy(node->name, name);
    return node;
}

void _print_node(tree_node_t *node, int depth)
{
    for (int i = 0; i < depth; i++)
    {
        printf("  ");
    }
    printf("%s\n", node->name);
    switch (node->node_type)
    {
    case LEX_NODE:
        localdebug("LEX_NODE\n");
        printf(" (%d)", node->line_index);
        break;
    case SYNTAX_NODE:
        localdebug("SYNTAX_NODE\n");
        switch (node->data.data_type)
        {
        case INT_DATA:
            localdebug("INT\n");
            printf(" %d", *(int *)node->data.data);
            break;
        case FLOAT_DATA:
            localdebug("FLOAT\n");
            printf(" %f", *(float *)node->data.data);
            break;
        case STRING_DATA:
            localdebug("STRING\n");
            printf(" %s", (char *)node->data.data);
            break;
        default:
            fprintf(stderr, "Error: data_type is not defined.\n");
            break;
        }
        break;
    default:
        fprintf(stderr, "Error: node_type is not defined.\n");
        break;
    }
    printf("\n");
}

void _free_node(tree_node_t *node, int depth)
{
    if (node->data.data != NULL)
    {
        free(node->data.data);
    }
    free(node);
}

void traval_tree_preorder(tree_t root, traval_func_t func, int depth)
{
    func(root, depth);
    for (tree_node_t *node = root->childrens; node != NULL; node = node->childrens)
    {
        traval_tree_preorder(node, func, depth + 1);
    }
}

void traval_tree_inorder(tree_t root, traval_func_t func, int depth)
{
    // TBD
}

void traval_tree_postorder(tree_t root, traval_func_t func, int depth)
{
    for (tree_node_t *node = root->childrens; node != NULL; node = node->childrens)
    {
        traval_tree_preorder(node, func, depth + 1);
    }
    func(root, depth);
}

void traval_tree(tree_t root, traval_type_t traval_type, traval_func_t func)
{
    if (func == NULL)
    {
        func = _print_node;
    }
    switch (traval_type)
    {
    case TRAVAL_PREORDER:
        traval_tree_preorder(root, func, 0);
        break;
    case TRAVAL_INORDER:
        traval_tree_inorder(root, func, 0);
        break;
    case TRAVAL_POSTORDER:
        traval_tree_postorder(root, func, 0);
        break;
    default:
        fprintf(stderr, "Error: traval_type is not defined.\n");
        break;
    }
}


void destroy_tree(tree_t * ptree)
{
    tree_t root = *ptree;
    traval_tree(root, TRAVAL_POSTORDER, _free_node);
    *ptree = NULL;
}